package app

import (
	"context"
	"duanju-mq/pkg/log"
	"time"
)

type goroutine struct {
	id      uint64
	ctx     context.Context
	cancel  context.CancelFunc
	ectx    context.Context
	ecancel context.CancelFunc
	btime   time.Time
}

func (c *goroutine) ID() uint64           { return c.id }
func (c *goroutine) CTX() context.Context { return c.ctx }
func (c *goroutine) Cancel()              { c.cancel() }
func (c *goroutine) BeginTime() time.Time { return c.btime }
func (c *goroutine) OnEnded(fn func()) {
	Async(func() {
		<-c.ectx.Done()
		fn()
	})
}

type Goroutine interface {
	ID() uint64
	CTX() context.Context
	Cancel()
	BeginTime() time.Time
	OnEnded(fn func())
}

func newGoroutine(id uint64) *goroutine {
	g := &goroutine{id: id, btime: time.Now()}
	g.ctx, g.cancel = context.WithCancel(CTX)
	g.ectx, g.ecancel = context.WithCancel(CTX)
	return g
}

func Go(fn func(Goroutine)) Goroutine {
	ch := make(chan *goroutine)
	go func() {
		defer log.RecoverPanic()
		id := GoroutineID()
		g := newGoroutine(id)
		defer g.ecancel()
		ch <- g
		fn(g)
	}()
	return <-ch
}

// 确保异步执行完之前进程不会退出
// 必须响应全局app.CTX或app.GoContext().CTX()，否则wg死锁至强制退出
func GoSafe(fn func(Goroutine)) Goroutine {
	wg.Add(1)
	ch := make(chan *goroutine)
	go func() {
		defer log.RecoverPanic()
		defer wg.Done()
		id := GoroutineID()
		ctx := newGoroutine(id)
		defer ctx.ecancel()
		ch <- ctx
		fn(ctx)
	}()
	return <-ch
}
